package com.hyq.grid.common;

import org.gdal.gdal.gdal;
import org.gdal.ogr.*;
import org.gdal.osr.SpatialReference;

import java.awt.geom.Point2D;
import java.util.*;


public class tools {
    public static Set<Integer> getSameElement(Integer[] a, Integer[] b) {

        Set<Integer> same = new HashSet<>();  //用来存放两个数组中相同的元素
        Set<Integer> temp = new HashSet<>();  //用来存放数组a中的元素


        for (int i = 0; i < a.length; i++) {
            temp.add(a[i]);   //把数组a中的元素放到Set中，可以去除重复的元素
        }

        for (int j = 0; j < b.length; j++) {
            //把数组b中的元素添加到temp中
            //如果temp中已存在相同的元素，则temp.add（b[j]）返回false
            if (!temp.add(b[j]))
                same.add(b[j]);
        }
        return same;
    }

    public static Set<Integer> getSameElementNew(int[] a, int[] b) {

        Set<Integer> same = new HashSet<>();  //用来存放两个数组中相同的元素
        Set<Integer> temp = new HashSet<>();  //用来存放数组a中的元素


        for (int i = 0; i < a.length; i++) {
            temp.add(a[i]);   //把数组a中的元素放到Set中，可以去除重复的元素
        }

        for (int j = 0; j < b.length; j++) {
            //把数组b中的元素添加到temp中
            //如果temp中已存在相同的元素，则temp.add（b[j]）返回false
            if (!temp.add(b[j]))
                same.add(b[j]);
        }
        return same;
    }

    /**
     * 获取两数组中不相同给的数据
     *
     * @param t1  原始数组1
     * @param t2  原始数组1
     * @param <T> 数据类型
     * @return 不相同数据
     */
    public static <T> List<T> getDiffElement(T[] t1, T[] t2) {
        List<T> list1 = Arrays.asList(t1); //将t1数组转成list数组
        List<T> list2 = new ArrayList<T>();//用来存放2个数组中不相同的元素
        for (T t : t2) {
            if (!list1.contains(t)) {
                list2.add(t);
            }
        }
        return list2;
    }

    /**
     * 判断点是否在多边形内
     *
     * @param point 点
     * @param pts   多边形点列表
     * @return 点在多边形上或在多边形内部返回 true 否则返回false
     */
    public static boolean IsPtInPoly(Point2D.Double point, List<Point2D.Double> pts) {

        int N = pts.size();
        boolean boundOrVertex = true; //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
        int intersectCount = 0;//cross points count of x
        double precision = 2e-10; //浮点类型计算时候与0比较时候的容差
        Point2D.Double p1, p2;//neighbour bound vertices
        Point2D.Double p = point; //当前点

        p1 = pts.get(0);//left vertex
        for (int i = 1; i <= N; ++i) {//check all rays
            if (p.equals(p1)) {
                return boundOrVertex;//p is an vertex
            }

            p2 = pts.get(i % N);//right vertex
            if (p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)) {//ray is outside of our interests
                p1 = p2;
                continue;//next ray left point
            }

            if (p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)) {//横坐标 内  ray is crossing over by the algorithm (common part of)
                if (p.y <= Math.max(p1.y, p2.y)) {//y下  x is before of ray
                    if (p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)) {//overlies on a horizontal ray  垂线
                        return boundOrVertex;
                    }

                    if (p1.y == p2.y) {//水平线 ray is vertical
                        if (p1.y == p.y) {//水平线内 overlies on a vertical ray
                            return boundOrVertex;
                        } else {//before ray
                            ++intersectCount;  //交点在上方
                        }
                    } else {//cross point on the left side
                        double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;//两点式化简，交点y坐标 cross point of y
                        if (Math.abs(p.y - xinters) < precision) {//== 0  在线上  overlies on a ray
                            return boundOrVertex;
                        }

                        if (p.y < xinters) {//before ray
                            ++intersectCount;  //交点在上方
                        }
                    }
                }
            } else {//special case when ray is crossing through the vertex
                if (p.x == p2.x && p.y <= p2.y) {//p crossing over p2
                    Point2D.Double p3 = pts.get((i + 1) % N); //next vertex
                    if (p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)) {//p.x lies between p1.x & p3.x
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;//next ray left point
        }

        if (intersectCount % 2 == 0) {//偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }

    }

    /**
     * 求数组最小值
     *
     * @param arraydata 原始数组
     * @return 最小值
     */
    public static Double MinofArray(Double[] arraydata) {
        Double min = arraydata[0];
        for (Double element : arraydata) {
            if (element < min)
                min = element;
        }
        return min;
    }

    /**
     * 求数组最大值
     *
     * @param arraydata 原始数组
     * @return 最大值
     */
    public static Double MaxofArray(Double[] arraydata) {
        double max = arraydata[0];
        for (Double element : arraydata) {
            if (element > max)
                max = element;
        }
        return max;
    }


    /**
     * @param a         需要移动的数据
     * @param arraydata 需要移动的数组
     * @return 将数组中的某个数与第一个数调换位置（移动到首位）并返回该数组，如果数组中没有该数返回 null，如果数组包含多个a，则将啊第一次出现的位置与首位调换
     */
    public static Integer[] MoveToFirst(Integer a, Integer[] arraydata) {

        int indexOfa = -1;
        for (int i = 0; i < arraydata.length; i++) {
            if (a.equals(arraydata[i])) {
                indexOfa = i;
                break;
            }
        }

        if (indexOfa == -1) {
            return null;
        } else {
            Integer tempa = arraydata[0];
            arraydata[0] = arraydata[indexOfa];
            arraydata[indexOfa] = tempa;
            return arraydata;
        }
    }

    /**
     * 数组是否包含某个数
     *
     * @param a         需要查找的数
     * @param arraydata 原始数组
     * @return 包含返回true 否则false
     */
    public static boolean BlIsContain(Integer a, Integer[] arraydata) {


        for (int i = 0; i < arraydata.length; i++) {
            if (a.equals(arraydata[i])) {
                return true;
            }
        }

        return false;
    }

    /**
     * 求三角形外接圆圆心
     * 2*(x2-x1)*X + 2*(y2-y1)*Y = x2*x2 + y2*y2 - x1*x1 - y1*y1
     * 2*(x3-x1)*X + 2*(y3-y1)*Y = x3*x3 + y3*y3 - x1*x1 - y1*y1
     *
     * @param points 三角形坐标
     * @return 外接圆圆心坐标
     */
    public static Double[] OutCenterTriangle(List<Double[]> points) {
        Double[] center = new Double[2];
        if (points.size() != 3) {
            return null;
        }
        Double a1, b1, c1, a2, b2, c2;


        a1 = 2 * (points.get(1)[0] - points.get(0)[0]);
        b1 = 2 * (points.get(1)[1] - points.get(0)[1]);
        c1 = points.get(1)[0] * points.get(1)[0] + Math.pow(points.get(1)[1], 2) - Math.pow(points.get(0)[0], 2) - Math.pow(points.get(0)[1], 2);

        a2 = 2 * (points.get(2)[0] - points.get(0)[0]);
        b2 = 2 * (points.get(2)[1] - points.get(0)[1]);
        c2 = Math.pow(points.get(2)[0], 2) + Math.pow(points.get(2)[1], 2) - Math.pow(points.get(0)[0], 2) - Math.pow(points.get(0)[1], 2);

        //ax + by = c

        center[1] = (a2 * c1 - a1 * c2) / (a2 * b1 - a1 * b2);
        if (a2.equals(.0) || a2.equals(0)) {
            center[0] = (c1 - b1 * center[1]) / a1;
        } else {
            center[0] = (c2 - b2 * center[1]) / a2;
        }

        return center;
    }

    /**
     * @param points 三角形点列表
     * @return 返回三角形的内心坐标
     */
    public static Double[] IncenterTriangle(List<Double[]> points) {
        if (points.size() != 3) {
            System.out.println("ERROR! points.size = " + points.size() + " ,不满足构建一个三角形的条件!");
            return null;
        }
        Double x1 = points.get(0)[0];
        Double y1 = points.get(0)[1];

        Double x2 = points.get(1)[0];
        Double y2 = points.get(1)[1];

        Double x3 = points.get(2)[0];
        Double y3 = points.get(2)[1];
        Double a = Math.sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3));
        Double b = Math.sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3));
        Double c = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));

        Double x = (a * x1 + b * x2 + c * x3) / (a + b + c);
        Double y = (a * y1 + b * y2 + c * y3) / (a + b + c);
        return new Double[]{x, y};
    }

    public static double[] IncenterTriangleNew(List<double[]> points) {
        if (points.size() != 3) {
            return null;
        }
        Double x1 = points.get(0)[0];
        Double y1 = points.get(0)[1];

        Double x2 = points.get(1)[0];
        Double y2 = points.get(1)[1];

        Double x3 = points.get(2)[0];
        Double y3 = points.get(2)[1];
        Double a = Math.sqrt((x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3));
        Double b = Math.sqrt((x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3));
        Double c = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));

        Double x = (a * x1 + b * x2 + c * x3) / (a + b + c);
        Double y = (a * y1 + b * y2 + c * y3) / (a + b + c);
        return new double[]{x, y};
    }


    public static Double[] IncentreOfPolygon(List<Double[]> points) {

        if (points.size() == 2) {
            Double[] center1 = points.get(0);
            Double[] center2 = points.get(1);
            Double[] polygonCenter = new Double[]{(center1[0] + center2[0]) / 2.0, (center1[1] + center2[1]) / 2.0};
            return polygonCenter;
        }
        if (points.size() == 1) {
            return points.get(0);
        }


        List<Double[]> triangleCenter = new ArrayList<>();
        Double[] startPoint = points.get(0);
        for (int i = 1; i < (points.size() - 1); i++) {
//            构建三角形
            List<Double[]> tempTrianglePoints = new ArrayList<>();
            tempTrianglePoints.add(startPoint);
            tempTrianglePoints.add(points.get(i));
            tempTrianglePoints.add(points.get(i + 1));

//            存储三角形内心
            triangleCenter.add(IncenterTriangle(tempTrianglePoints));
        }
        if (triangleCenter.size() >= 3) {
            return IncentreOfPolygon(triangleCenter);
        } else if (triangleCenter.size() == 2) {
            Double[] center1 = triangleCenter.get(0);
            Double[] center2 = triangleCenter.get(1);
            Double[] polygonCenter = new Double[]{(center1[0] + center2[0]) / 2.0, (center1[1] + center2[1]) / 2.0};
            return polygonCenter;
        } else {
            return triangleCenter.get(0);
        }

//        //            构建三角形
//        Double[] startPoint = points.get(0);
//        List<Double[]> tempTrianglePoints = new ArrayList<>();
//        tempTrianglePoints.add(startPoint);
//        tempTrianglePoints.add(points.get(1));
//        tempTrianglePoints.add(points.get(2));

//            存储三角形内心
//       return  IncenterTriangle(tempTrianglePoints);

    }

    /**
     * @param searchData 需要计数的数值
     * @param data       数值列表
     * @return 返回 searchData 在 data中出现的次数
     */
    public static Integer SameCount(Integer searchData, List<Integer> data) {
        Integer count = 0;
        for (int i = 0; i < data.size(); i++) {
            if (searchData.equals(data.get(i))) count++;
        }
        return count;
    }


    /**
     * @param path 数据存放的路径
     * @param data 需要存储的数据
     * @return Shapefile存储成功返回 true,否则返回 false
     */
    public static Boolean WriteShapefiles(String path, List<Map> data) {
        String strVectorFilePath = path;

//        注册组件
        ogr.RegisterAll();
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
        gdal.SetConfigOption("SHAPE_ENCODING", "CP936");

//        创建数据驱动
        String strDriverName = "ESRI Shapefile";
        org.gdal.ogr.Driver oDriver = ogr.GetDriverByName(strDriverName);
        if (oDriver == null) {
            System.out.println(strVectorFilePath + " 驱动不可用！\n");
            return false;
        }
//        创建数据集
        DataSource oDS = oDriver.CreateDataSource(strVectorFilePath, null);
        if (oDS == null) {
            System.out.println("创建矢量文件【" + strVectorFilePath + "】失败！\n");
            return false;
        }

//        创建图层
//        设置坐标系
        SpatialReference spatialReference = new SpatialReference("");
        spatialReference.SetWellKnownGeogCS("WGS84");

        Layer oLayerPoints = oDS.CreateLayer("Points", spatialReference, ogr.wkbPoint, null);
        Layer oLayerPolygon = oDS.CreateLayer("Polygon", spatialReference, ogr.wkbPolygon, null);

        if (oLayerPoints == null || oLayerPolygon == null) {
            System.out.println("图层创建失败！\n");
            return false;
        }
        // 创建属性表
        // 先创建一个叫FieldID的整型属性
        FieldDefn oFieldID = new FieldDefn("FieldID", ogr.OFTInteger);
        oLayerPoints.CreateField(oFieldID);
        oLayerPolygon.CreateField(oFieldID);

        // 再创建一个叫FeatureName的字符型属性，字符长度为50
        FieldDefn oFieldName = new FieldDefn("FieldName", ogr.OFTString);
        oFieldName.SetWidth(100);
        oLayerPoints.CreateField(oFieldName);
        oLayerPolygon.CreateField(oFieldName);

        // 再创建一个叫Height的双精度属性
        FieldDefn oFieldHeight = new FieldDefn("Height", ogr.OFTReal);
        oLayerPolygon.CreateField(oFieldHeight);
        oLayerPoints.CreateField(oFieldHeight);


        FeatureDefn oDefnPoints = oLayerPoints.GetLayerDefn();
        FeatureDefn oDefnPolygon = oLayerPolygon.GetLayerDefn();

        for (Map meshCell : data) {
            Double[] x = (Double[]) meshCell.get("X");
            Double[] y = (Double[]) meshCell.get("Y");
            Double z = (Double) meshCell.get("Z");

            // 创建网格单元中心点
            Double centerX = (MaxofArray(x) + MinofArray(x)) / 2.0;
            Double centerY = (MaxofArray(y) + MinofArray(y)) / 2.0;
            Feature oFeaturePoints = new Feature(oDefnPoints);
            oFeaturePoints.SetField(0, 0);
            oFeaturePoints.SetField(1, "点");
            oFeaturePoints.SetField(2, z);
            Geometry geomPoints = new Geometry(ogr.wkbPoint);
            geomPoints.AddPoint(centerX, centerY);
            oFeaturePoints.SetGeometry(geomPoints);
            oLayerPoints.CreateFeature(oFeaturePoints);

//            创建网格单元
            Feature oFeaturePolygon = new Feature(oDefnPolygon);
            oFeaturePolygon.SetField(0, 0);
            oFeaturePolygon.SetField(1, "多边形");
            oFeaturePolygon.SetField(2, z);
            String points = "";
            for (int i = 0; i < x.length; i++) {
                points += x[i] + " " + y[i] + ",";
            }
//            闭合多边形
            points += x[0] + " " + y[0];
            Geometry geomPolygon = Geometry.CreateFromWkt("POLYGON ((" + points + "))");
            oFeaturePolygon.SetGeometry(geomPolygon);
            oLayerPolygon.CreateFeature(oFeaturePolygon);
        }

        oDS.SyncToDisk();
        System.out.println("\n数据集创建完成！\n");
        return true;
    }


    /**
     * @param x          网格单元的x坐标序列
     * @param y          网格单元的y坐标序列
     * @param startPoint 箱式索引中最小的坐标点(minX,minY)
     * @param precision  箱子单元大小
     * @param xSize      索引宽度
     * @param ySize      索引高度
     * @param cellIndex  当前单元的索引
     * @return 返回包含网格单元顶点的箱子单元索引
     */
    public static List<Integer> GetBoxIndex(double[] x, double[] y, Point2D.Double startPoint, Double precision, int xSize, int ySize, int[] cellIndex) {


        List<Integer> boxIndex = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
//                剔除边界单元
                if ((cellIndex[0] - 1 + i) < 0 || (cellIndex[1] - 1 + j) < 0) continue;
                if ((cellIndex[0] - 1 + i) >= xSize || (cellIndex[1] - 1 + j) >= ySize) continue;
                if (i == 1 && j == 1) continue;
                List<Point2D.Double> boxCell = GetBoxCellCoordinates(cellIndex[0] - 1 + i, cellIndex[1] - 1 + j, startPoint, precision);

//                for (int k = 0; k < x.length; k++) {
//
//                    if (IsPtInPoly(new Point2D.Double(x[k], y[k]), boxCell)) {
//                        boxIndex.add((cellIndex[1] - 1 + j) * xSize + cellIndex[0] - 1 + i);
//                        break;
//                    }
//                }

                for (int k = 0; k < x.length; k++) {
                    List<Point2D.Double> tempLine = new ArrayList<>();
                    if (k == x.length - 1) {
                        tempLine.add(new Point2D.Double(x[k], y[k]));
                        tempLine.add(new Point2D.Double(x[0], y[0]));
                    } else {
                        tempLine.add(new Point2D.Double(x[k], y[k]));
                        tempLine.add(new Point2D.Double(x[k + 1], y[k + 1]));
                    }

                    if (IsLineIntersectPolygon(tempLine, boxCell) != 0) {
                        boxIndex.add((cellIndex[1] - 1 + j) * xSize + cellIndex[0] - 1 + i);
                        break;
                    }
                }
            }
        }
        boxIndex.add(cellIndex[1] * xSize + cellIndex[0]);

        return boxIndex;
    }

    public static List<Integer> GetBoxIndex(Double[] x, Double[] y, Point2D.Double startPoint, Double precision, int xSize, int ySize, int[] cellIndex) {


        List<Integer> boxIndex = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
//                剔除边界单元
                if ((cellIndex[0] - 1 + i) < 0 || (cellIndex[1] - 1 + j) < 0) continue;
                if ((cellIndex[0] - 1 + i) >= xSize || (cellIndex[1] - 1 + j) >= ySize) continue;
                if (i == 1 && j == 1) continue;
                List<Point2D.Double> boxCell = GetBoxCellCoordinates(cellIndex[0] - 1 + i, cellIndex[1] - 1 + j, startPoint, precision);

//                for (int k = 0; k < x.length; k++) {
//
//                    if (IsPtInPoly(new Point2D.Double(x[k], y[k]), boxCell)) {
//                        boxIndex.add((cellIndex[1] - 1 + j) * xSize + cellIndex[0] - 1 + i);
//                        break;
//                    }
//                }

                for (int k = 0; k < x.length; k++) {
                    List<Point2D.Double> tempLine = new ArrayList<>();
                    if (k == x.length - 1) {
                        tempLine.add(new Point2D.Double(x[k], y[k]));
                        tempLine.add(new Point2D.Double(x[0], y[0]));
                    } else {
                        tempLine.add(new Point2D.Double(x[k], y[k]));
                        tempLine.add(new Point2D.Double(x[k + 1], y[k + 1]));
                    }

                    if (IsLineIntersectPolygon(tempLine, boxCell) != 0) {
                        boxIndex.add((cellIndex[1] - 1 + j) * xSize + cellIndex[0] - 1 + i);
                        break;
                    }
                }
            }
        }
        boxIndex.add(cellIndex[1] * xSize + cellIndex[0]);

        return boxIndex;
    }

    /**
     * @param i          当前网格单元x方向索引
     * @param j          当前网格单元y方向索引
     * @param startPoint 箱式索引中最小的坐标点(minX,minY)
     * @param precision  箱子单元大小
     * @return 返回箱子单元四个角的坐标
     */
    public static ArrayList<Point2D.Double> GetBoxCellCoordinates(int j, int i, Point2D.Double startPoint, Double precision) {
        ArrayList<Point2D.Double> boxCell = new ArrayList<>();
        boxCell.add(new Point2D.Double(startPoint.x + j * precision, startPoint.y + i * precision));
        boxCell.add(new Point2D.Double(startPoint.x + (j + 1) * precision, startPoint.y + i * precision));
        boxCell.add(new Point2D.Double(startPoint.x + (j + 1) * precision, startPoint.y + (i + 1) * precision));
        boxCell.add(new Point2D.Double(startPoint.x + j * precision, startPoint.y + (i + 1) * precision));
        return boxCell;
    }

    /**
     * @param line    需要判断的线段(point1, point2)
     * @param polygon 需要判断的多边形(point1, point2, point3...)
     * @return 多边形包含线段返回 2, 相交返回 1, 不相关返回 0
     */
    public static int IsLineIntersectPolygon(List<Point2D.Double> line, List<Point2D.Double> polygon) {
        if (line.size() < 2 || polygon.size() < 3) return 0;

        Point2D.Double point1 = line.get(0);
        Point2D.Double point2 = line.get(1);


        int poinInPolygonNum = 0;
        if (IsPtInPoly(point1, polygon))
            poinInPolygonNum++;

        if (IsPtInPoly(point2, polygon))
            poinInPolygonNum++;

        if (poinInPolygonNum == 2) {
            // 两个点都在多边形内
            return 2;
        } else if (poinInPolygonNum == 1) {
            // 一个点在多边形内
            return 1;
        } else {
            //没有点在多边形内
            //判断线与多边形的边相交情况
            for (int i = 0; i < polygon.size() - 1; i++) {
                List<Point2D.Double> tempLine = new ArrayList<>();
                tempLine.add(polygon.get(i));
                tempLine.add(polygon.get(i + 1));
                if (IsLineIntersectLine(line, tempLine)) return 1;
            }
            return 0;
        }
    }

    /**
     * @param line1 第一条线段(point1，point2)
     * @param line2 第二条线段(point1，point2)
     * @return 两条线段相交返回true否则返回false
     */
    public static boolean IsLineIntersectLine(List<Point2D.Double> line1, List<Point2D.Double> line2) {
        if (line1.size() < 2 || line2.size() < 2) return false;
        double p0_x = line1.get(0).x;
        double p0_y = line1.get(0).y;
        double p1_x = line1.get(1).x;
        double p1_y = line1.get(1).y;

        double p2_x = line2.get(0).x;
        double p2_y = line2.get(0).y;
        double p3_x = line2.get(1).x;
        double p3_y = line2.get(1).y;

        double s02_x, s02_y, s10_x, s10_y, s32_x, s32_y, s_numer, t_numer, denom;
        s10_x = p1_x - p0_x;
        s10_y = p1_y - p0_y;
        s32_x = p3_x - p2_x;
        s32_y = p3_y - p2_y;

        denom = s10_x * s32_y - s32_x * s10_y;
        if (denom == 0)
            //平行或共线
            return false; // Collinear
        boolean denomPositive = denom > 0;

        s02_x = p0_x - p2_x;
        s02_y = p0_y - p2_y;
        s_numer = s10_x * s02_y - s10_y * s02_x;
        if (s_numer < 0 == denomPositive)
            //参数是大于等于0且小于等于1的，分子分母必须同号且分子小于等于分母
            return false; // No collision

        t_numer = s32_x * s02_y - s32_y * s02_x;
        if (t_numer < 0 == denomPositive) return false; // No collision

        if (
                Math.abs(s_numer) > Math.abs(denom) ||
                        Math.abs(t_numer) > Math.abs(denom)
        )
            return false;


        return true;
    }

    /**
     * @param node     网格点索引 [[point1.x,point1.y], [point2.x,point2.y], ...]
     * @param element  网格单元索引 [[point1.index,point2.index, ...], [point1.index,point2.index, ...], ...]
     * @param boxIndex 网格箱式索引 {"boxes": List<List>, "XLength": int, "YLength": int, "precision": Double, "startPoint": Point2D.Double}
     * @return 建立起网格单元的邻接信息[[neighborElement1.index1, neighborElement1.index2, ..], [neighborElement2.index1, neighborElement2.index2, ..]...]
     */
    public static List<List> CreatNeighborList(List<Double[]> node, List<Integer[]> element, Map boxIndex) {
//        获取箱式索引信息
        List<List> boxes = (List<List>) boxIndex.get("boxes"); //箱子单元存储的网格单元信息
        int xLength = (int) boxIndex.get("XLength"); // 索引长度
        int yLength = (int) boxIndex.get("YLength"); // 索引宽度
        Double precision = (Double) boxIndex.get("precision"); // 箱子单元大小
        Point2D.Double startPoint = (Point2D.Double) boxIndex.get("startPoint"); //索引起始点

        System.out.println("xLength:" + xLength + " yLength：" + yLength);


        List<List> neighborList = new ArrayList<>();
        for (int i = 0; i < element.size(); i++) {

            List<Double[]> currentElementCoordinates = new ArrayList<>();

//            获取当前单元坐标信息
            for (int j = 0; j < element.get(i).length; j++) {

                if (element.get(i)[j] == null) {
                    System.out.println("ERROR! 超出索引范围！" + element.get(i)[j]);
                }
                currentElementCoordinates.add(new Double[]{node.get(element.get(i)[j])[0], node.get(element.get(i)[j])[1]});
            }
            if (currentElementCoordinates.size() < 3) continue;
//            求当前单元(三角形)内心,并找到箱子
//            Double[] trangleCenter = IncenterTriangle(currentElementCoordinates);
            Double[] polygonCneter = IncentreOfPolygon(currentElementCoordinates);
            int xCount = (int) Math.floor((polygonCneter[0] - startPoint.x) / precision);
            int yCount = (int) Math.floor((polygonCneter[1] - startPoint.y) / precision);

            if (xCount < 0 || yCount < 0 || xCount >= xLength || yCount >= yLength) {
                System.out.println("ERROR!  单元:" + i + "箱式索引为：" + xCount + ", " + yCount);
                continue;
            }

//            获取周围箱子单元所包含的网格单元
            List<Integer> arroundBoxContianAll = new ArrayList<>();
            for (int j = -1; j <= 1; j++) {
                for (int k = -1; k <= 1; k++) {


                    int tempXCount = xCount + j;
                    int tempYCount = yCount + k;

//                    剔除超过便捷范围的索引
                    if (tempXCount < 0 || tempYCount < 0 || tempXCount >= xLength || tempYCount >= yLength) {
//                        System.out.println("ERROR!  单元:" + i + "箱式索引为：" + (xCount + j) + ", " + (yCount + k));
                        continue;
                    }
                    arroundBoxContianAll.addAll(boxes.get(tempYCount * xLength + tempXCount));
                }
            }

//            去除重复的网格单元
            LinkedHashSet<Integer> hashSet = new LinkedHashSet<>(arroundBoxContianAll);
            List<Integer> arroundBoxContian = new ArrayList<>(hashSet);

            Map<String, Object> tempNeighborMap = new HashMap<>();


            List<Integer> tempNeighborList = new ArrayList<>();
            List<Integer[]> tempNeighborListOri = new ArrayList<>();

            Integer[] currentElement = element.get(i);
            tempNeighborListOri.add(currentElement);

            for (int j = 0; j < arroundBoxContian.size(); j++) {
                Integer[] tempElement = element.get(arroundBoxContian.get(j));
                tempNeighborListOri.add(tempElement);

                Set<Integer> tempsame = tools.getSameElement(currentElement, tempElement);
                if (tempsame.size() == 2) {
                    tempNeighborList.add(arroundBoxContian.get(j));
                }
            }

            tempNeighborMap.put("elementID", tempNeighborListOri);
            tempNeighborMap.put("neighborID", tempNeighborList);
            neighborList.add(tempNeighborList);


        }
        return neighborList;
    }

    /**
     * @param array   需要判断的List
     * @param element 被判断的元素
     * @return 如果array包含element，返回索引，否则返回 -1
     * @description 添加了容差0.05, 可根据实际情况调整
     */
    public static int IsArrayIncludes(List<Double[]> array, Double[] element) {
        for (int i = 0; i < array.size(); i++) {
            Double[] tempElement = array.get(i);
            double tolerancex = Math.abs(tempElement[0] - element[0]);
            double tolerancey = Math.abs(tempElement[1] - element[1]);
            if (tolerancex <= 0.05 && tolerancey <= 0.05) {
                return i;
            }
        }
        return -1;
    }


    /**
     * 点集中是否包含某个点
     *
     * @param points
     * @param point
     * @return
     */
    public static int IsArrayIncludes(List<Point2D.Double> points, Point2D.Double point) {
        for (int i = 0; i < points.size(); i++) {
            Point2D.Double tempPoint = points.get(i);
            double tolerancex = Math.abs(tempPoint.x - point.x);
            double tolerancey = Math.abs(tempPoint.y - point.y);
            if (tolerancex <= 0.05 && tolerancey <= 0.05) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 打印数组信息
     *
     * @param a
     */
    public static void PrintArray(Integer[] a) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < a.length; i++) {
            if (i == (a.length - 1)) {
                stringBuilder.append(a[i].toString());
            } else {
                stringBuilder.append(a[i].toString() + " ");
            }
        }

        System.out.println(stringBuilder);
    }


    /**
     * 高斯正算
     *
     * @param longitude 经度(-PI , Pi)
     * @param latitude  纬度 (-PI/2 , PI/2)
     * @return 投影坐标XY
     */
    public static double[] BLToGauss(double longitude, double latitude) {

        double p0 = 180 / Math.PI;
        double p1 = 60 * 180 / Math.PI;
        double p2 = 60 * 60 * 180 / Math.PI;
        int zonWidth = 6; // 带宽

        if (longitude < 0) {
            longitude = longitude + 2 * Math.PI;
        }

        /**第一步 根据经纬度计算中央子午线精度差 */
        int l0 = ((int) (longitude * p0 / zonWidth) + 1) * zonWidth - 3; // 6°带中央子午线
        double l = (longitude * p0 - l0) / p0; //精度差

//        double a = 6378140.0; //椭圆长半轴
//        double b = 6356755.288157528 ; //椭圆短半轴
        double a = 100;
        double b = 100;
        double e1 = Math.sqrt(a * a - b * b) / a; //椭圆第一偏心率
        double e2 = Math.sqrt(a * a - b * b) / b; //椭圆第二偏心率

        /**第二步 根据椭球选择计算子午弧长公式 */
        // 子午圈曲率半径展开系数
        double m0 = a * (1 - e1 * e1);
        double m2 = 3 * e1 * e1 * m0 / 2;
        double m4 = 5 * e1 * e1 * m2 / 4;
        double m6 = 7 * e1 * e1 * m4 / 6;
        double m8 = 9 * e1 * e1 * m6 / 8;

        double a0 = m0 + m2 / 2 + 3 * m4 / 8 + 5 * m6 / 16 + 35 * m8 / 128;
        double a2 = m2 / 2 + m4 / 2 + 15 * m6 / 32 + 7 * m8 / 16;
        double a4 = m4 / 8 + 3 * m6 / 16 + 7 * m8 / 32;
        double a6 = m6 / 32 + m8 / 16;
        double a8 = m8 / 128;

        double X = a0 * latitude - a2 * Math.sin(2 * latitude) / 2 + a4 * Math.sin(4 * latitude) / 4 - a6 * Math.sin(6 * latitude) / 6 + a8 * Math.sin(8 * latitude) / 8;
//        double X = 111133.861 * latitude - 16038.480 * Math.sin(2 * latitude) + 16.828 * Math.sin(4 * latitude) - 0.022 * Math.sin(6 * latitude);
        /** 第三步 计算正算公式中的各符号值*/
        double t = Math.tan(latitude);
        double fi = e2 * Math.cos(latitude);
        double N = a / Math.sqrt(1 - e1 * e1 * Math.sin(latitude) * Math.sin(latitude));

        /** 第四步 带入公式求高斯平面坐标*/
        double x = X + N * Math.sin(latitude) * Math.cos(latitude) * l * l / 2 + N * Math.sin(latitude) * Math.pow(Math.cos(latitude), 3) * (5 - t * t - 9 * fi * fi) * Math.pow(l, 4) / 24;
        double y = N * Math.cos(latitude) * l + N * Math.pow(Math.cos(latitude), 3) * (1 - t * t + fi * fi) * Math.pow(l, 3) / 6 + N * Math.pow(Math.cos(latitude), 5) * (5 - 18 * t * t + Math.pow(t, 4)) * Math.pow(l, 5) / 120;

        return new double[]{x, y};
    }

    /**
     * 高斯正算
     *
     * @param longitude 经度(-180°, 180°)
     * @param latitude  纬度(-90°, 90°)
     * @return 高斯投影坐标XY
     */
    public static double[] gaussBLtoXY(double longitude, double latitude) {

        if (longitude < 0) {
            longitude = longitude + 360;
        }
        int ProjNo = 0;

        // 带宽
        int ZoneWide = 6;

        double longitude1, latitude1, longitude0, X0, Y0, xval, yval;
        double a, f, e2, ee, NN, T, C, A, M, iPI;

        // 3.1415926535898/180.0;
        iPI = 0.0174532925199433;

        // 54年北京坐标系参数
        a = 6378245.0;
        f = 1.0 / 298.3;

        // 80年西安坐标系参数
        // a=6378140.0;
        // f=1/298.257;

        ProjNo = (int) (longitude / ZoneWide);
        longitude0 = ProjNo * ZoneWide + ZoneWide / 2;
        longitude0 = longitude0 * iPI;

        // 经度转换为弧度
        longitude1 = longitude * iPI;

        // 纬度转换为弧度
        latitude1 = latitude * iPI;

        e2 = 2 * f - f * f;
        ee = e2 * (1.0 - e2);
        NN = a / Math.sqrt(1.0 - e2 * Math.sin(latitude1) * Math.sin(latitude1));
        T = Math.tan(latitude1) * Math.tan(latitude1);
        C = ee * Math.cos(latitude1) * Math.cos(latitude1);
        A = (longitude1 - longitude0) * Math.cos(latitude1);
        M = a * ((1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256) * latitude1 - (3 * e2 / 8 + 3 * e2 * e2 / 32 + 45 * e2 * e2 * e2 / 1024) * Math.sin(2 * latitude1) + (15 * e2 * e2 / 256 + 45 * e2 * e2 * e2 / 1024) * Math.sin(4 * latitude1) - (35 * e2 * e2 * e2 / 3072) * Math.sin(6 * latitude1));
        xval = NN * (A + (1 - T + C) * A * A * A / 6 + (5 - 18 * T + T * T + 72 * C - 58 * ee) * A * A * A * A * A / 120);
        yval = M + NN * Math.tan(latitude1) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 + (61 - 58 * T + T * T + 600 * C - 330 * ee) * A * A * A * A * A * A / 720);
        X0 = 1000000 * (ProjNo + 1) + 500000;
        Y0 = 0;
        xval = xval + X0;
        yval = yval + Y0;
        return new double[]{yval, xval};
    }

    /**
     * 高斯反算
     *
     * @param X 高斯投影坐标 X
     * @param Y 高斯投影坐标 Y
     * @return 经纬度坐标
     */
    public static double[] gaussXYtoBL(double X, double Y) {
        int ProjNo;
        int ZoneWide; // //带宽
        double[] output = new double[2];
        double longitude1, latitude1, longitude0, Y0, X0, yval, xval;// latitude0,
        double e1, e2, f, a, ee, NN, T, C, M, D, R, u, fai, iPI;
        iPI = 0.0174532925199433; // //3.1415926535898/180.0;
        a = 6378245.0;
        f = 1.0 / 298.3; //54年北京坐标系参数
        //a = 6378140.0;
        //f = 1 / 298.257; // 80年西安坐标系参数
        // 54年北京坐标系参数

        ZoneWide = 6; // //6度带宽
        ProjNo = (int) (Y / 1000000); // 查找带号
        longitude0 = (ProjNo - 1) * ZoneWide + ZoneWide / 2;
        longitude0 = longitude0 * iPI; // 中央经线

        Y0 = ProjNo * 1000000 + 500000;
        X0 = 0;
        yval = Y - Y0;
        xval = X - X0; // 带内大地坐标
        e2 = 2 * f - f * f;
        e1 = (1.0 - Math.sqrt(1 - e2)) / (1.0 + Math.sqrt(1 - e2));
        ee = e2 / (1 - e2);
        M = xval;
        u = M / (a * (1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256));
        fai = u + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * Math.sin(2 * u) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * Math.sin(4 * u) + (151 * e1 * e1 * e1 / 96) * Math.sin(6 * u) + (1097 * e1 * e1 * e1 * e1 / 512) * Math.sin(8 * u);
        C = ee * Math.cos(fai) * Math.cos(fai);
        T = Math.tan(fai) * Math.tan(fai);
        NN = a / Math.sqrt(1.0 - e2 * Math.sin(fai) * Math.sin(fai));
        R = a * (1 - e2) / Math.sqrt((1 - e2 * Math.sin(fai) * Math.sin(fai)) * (1 - e2 * Math.sin(fai) * Math.sin(fai)) * (1 - e2 * Math.sin(fai) * Math.sin(fai)));
        D = yval / NN;
        // 计算经度(Longitude) 纬度(Latitude)
        longitude1 = longitude0 + (D - (1 + 2 * T + C) * D * D * D / 6 + (5 - 2 * C + 28 * T - 3 * C * C + 8 * ee + 24 * T * T) * D * D * D * D * D / 120) / Math.cos(fai);
        latitude1 = fai - (NN * Math.tan(fai) / R) * (D * D / 2 - (5 + 3 * T + 10 * C - 4 * C * C - 9 * ee) * D * D * D * D / 24 + (61 + 90 * T + 298 * C + 45 * T * T - 256 * ee - 3 * C * C) * D * D * D * D * D * D / 720);
        // 转换为度 DD
        output[0] = longitude1 / iPI;
        output[1] = latitude1 / iPI;
        return output;
    }
    
}

